// 
//  Copyright © 2009 Jiří Zárevúcky <zarevucky.jiri@gmail.com>
// 
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU Affero General Public License as
//  published by the Free Software Foundation, either version 3 of the
//  License, or (at your option) any later version.
// 
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU Affero General Public License for more details.
// 
//  You should have received a copy of the GNU Affero General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
// 
// 


// TODO: update to rfc3920bis

using System;
using System.Collections.Generic;

using Galaxium.Protocol.Xmpp.Library.Xml;

namespace Galaxium.Protocol.Xmpp.Library.Core
{
	public class StanzaError: Element
	{
		private static Dictionary<string, string> s_human_texts;

		static StanzaError () {
			s_human_texts = new Dictionary<string, string> ();

			// cancel
			s_human_texts ["conflict"] = "Conflict."; 
			s_human_texts ["feature-not-implemented"] = "Feature is not implemented."; 
			s_human_texts ["item-not-found"] = "Item not found."; 
			s_human_texts ["not-allowed"] = "Action not allowed."; 
			s_human_texts ["remote-server-not-found"] = "Remote server not found.";
			s_human_texts ["service-unavailable"] = "Service is not available.";
			s_human_texts ["undefined-condition"] = "Undefined condition.";

			// modify
			s_human_texts ["bad-request"] = "Bad request."; 
			s_human_texts ["gone"] = "Entity is no longer available at this address."; 
			s_human_texts ["jid-malformed"] = "Malformed JID.";
			s_human_texts ["not-acceptable"] = "Not acceptable."; 
			s_human_texts ["redirect"] = "Redirect.";
			// TODO: silent temporary change of addressing on redirect error

			// auth
			s_human_texts ["forbidden"] = "Action is forbidden.";
			s_human_texts ["not-authorized"] = "You are not authorized.";
			s_human_texts ["payment-required"] = "Payment is required.";
			s_human_texts ["registration-required"] = "Registration is required.";
			s_human_texts ["subscription-required"] = "Subscription is required.";

			//wait
			s_human_texts ["internal-server-error"] = "Internal server error.";
			s_human_texts ["recipient-unavailable"] = "Recipient is not available."; 
			s_human_texts ["remote-server-timeout"] = "Remote server timed out.";
			s_human_texts ["resource-constraint"] = "Not enough system resources."; 
			s_human_texts ["unexpected-request"] = "Request wasn't expected.";
		}
		
		private static string ErrorType (string condition)
		{
			switch (condition) {
			case "conflict": 
			case "feature-not-implemented": 
			case "item-not-found": 
			case "not-allowed": 
			case "remote-server-not-found":
			case "service-unavailable":
			case "undefined-condition":
				return "cancel";
			case "bad-request": 
			case "gone": 
			case "jid-malformed":
			case "not-acceptable": 
			case "redirect":
				return "modify";
			case "forbidden":
			case "not-authorized":
			case "payment-required":
			case "registration-required":
			case "subscription-required":
				return "auth";
			case "internal-server-error": 
			case "recipient-unavailable": 
			case "remote-server-timeout":
			case "resource-constraint": 
			case "unexpected-request":
				return "wait";
			default:
				return "cancel";
			}
		}
		
#region Properties
		
		public string Type {
			get { return this ["type"]; }
		}
		
		public string Condition {
			get { return FirstChild (null, Namespaces.Stanzas).Name; }
		}
		
		public string Description {
			get { return GetTextChild ("text", Namespaces.Stanzas); }
		}
#endregion
		
		public StanzaError ()  // for cloning
			:base ("error")
		{
		}
		
		public StanzaError (string condition, string description)
			:base ("error")
		{
			if (condition == null)
				throw new ArgumentNullException ("condition");
			
			SetAttribute ("type", ErrorType (condition));
			AppendChild (new Element (condition, Namespaces.Stanzas));
			if (description != null)
				AppendChild (new Element ("text", Namespaces.Stanzas).SetText (description));
		}

		public StanzaError (Element source)
			:base (source.Clone ())
		{
			if (Name != "error")
				throw new ArgumentException ();
		}

		public string GetHumanRepresentation ()
		{
			var condition = Condition;
			return s_human_texts.ContainsKey (condition) ? s_human_texts [condition] : condition;
		}
		
		public string GetCondition (string xmlns)
		{
			var child = FirstChild (null, xmlns);
			return child == null ? null : child.Name;
		}
	}
}